<Project>

  <!--
    ##### Contract project settings #####
    - Allows referencing contract projects for passing matching reference assemblies to tooling like ApiCompat.
    - Allows building against contract assemblies when referencing source projects.
  -->

  <PropertyGroup>
    <ContractProject Condition="'$(ContractProject)' == ''">$(LibrariesProjectRoot)$(MSBuildProjectName)\ref\$(MSBuildProjectName).csproj</ContractProject>
    <HasMatchingContract Condition="'$(HasMatchingContract)' == '' and Exists('$(ContractProject)')">true</HasMatchingContract>
    <!-- Don't build against reference assemblies when projects are packable and the tfm is not the latest .NETCoreApp as
         such reference assemblies don't ship to customers and only exist for tooling scenarios. -->
    <AnnotateTargetPathWithContract Condition="'$(AnnotateTargetPathWithContract)' == '' and
                                               '$(HasMatchingContract)' == 'true' and
                                               (
                                                '$(IsPackable)' != 'true' or
                                                (
                                                 '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and
                                                 '$(TargetFrameworkVersion)' == 'v$(NetCoreAppCurrentVersion)'
                                                )
                                               )">true</AnnotateTargetPathWithContract>
  </PropertyGroup>

  <ItemGroup Condition="'$(HasMatchingContract)' == 'true' and '$(ContractProject)' != ''">
    <ProjectReference Include="$(ContractProject)" ReferenceOutputAssembly="false" OutputItemType="ResolvedMatchingContract" />
  </ItemGroup>

  <!-- We aren't referencing the contract, but make sure it's considered as an input to Compile so that if it changes we rebuild and rerun API compat -->
  <Target Name="AddResolvedMatchingContractToCompileInput"
          BeforeTargets="CoreCompile"
          AfterTargets="ResolveProjectReferences"
          Condition="'@(ResolvedMatchingContract)' != ''">
    <ItemGroup>
      <CustomAdditionalCompileInputs Include="@(ResolvedMatchingContract)" />
    </ItemGroup>
  </Target>

  <!-- Allow P2Ps that target a source project to build against the corresponding ref project. -->
  <Target Name="AnnotateTargetPathWithTargetPlatformMonikerWithReferenceAssembly"
          Condition="'$(AnnotateTargetPathWithContract)' == 'true'"
          DependsOnTargets="ResolveProjectReferences"
          AfterTargets="GetTargetPathWithTargetPlatformMoniker">
    <ItemGroup>
      <TargetPathWithTargetPlatformMoniker ReferenceAssembly="@(ResolvedMatchingContract)" />
    </ItemGroup>
  </Target>

  <!-- ##### APICompat settings ##### -->

  <PropertyGroup>
    <ApiCompatContractItemName>ResolvedMatchingContract</ApiCompatContractItemName>
    <ApiCompatStrictMode Condition="'$(ApiCompatStrictMode)' == ''">true</ApiCompatStrictMode>
    <ApiCompatUseRoslynToolsetPackagePath Condition="'$(DotNetBuildSourceOnly)' == 'true'">true</ApiCompatUseRoslynToolsetPackagePath>

    <!-- Optional rules -->
    <ApiCompatEnableRuleAttributesMustMatch>true</ApiCompatEnableRuleAttributesMustMatch>
    <ApiCompatEnableRuleCannotChangeParameterName>true</ApiCompatEnableRuleCannotChangeParameterName>

    <_ApiCompatCaptureGroupPattern>.+%5C$([System.IO.Path]::DirectorySeparatorChar)(.+)%5C$([System.IO.Path]::DirectorySeparatorChar)(.+)</_ApiCompatCaptureGroupPattern>
    <_ApiCompatRuntimePrefixPattern>(.+)/(net%5Cd.%5Cd)-(.+)/(.+)</_ApiCompatRuntimePrefixPattern>
    <_ApiCompatLibReplacementString>lib/$1/$2</_ApiCompatLibReplacementString>
    <!-- CoreLib source projects have different output paths. -->
    <_ApiCompatLibReplacementString Condition="'$(MSBuildProjectName)' == 'System.Private.CoreLib'">lib/$(NetCoreAppCurrent)/$2</_ApiCompatLibReplacementString>
  </PropertyGroup>

  <PropertyGroup>
    <IsRuntimeAndReferenceAssembly Condition="'$(IsRuntimeAndReferenceAssembly)' == '' and
                                              '$(HasMatchingContract)' != 'true' and
                                              '$(IsPrivateAssembly)' != 'true'">true</IsRuntimeAndReferenceAssembly>
  </PropertyGroup>

  <!-- Disable suppression validation for packable projects that have a corresponding reference source project.
       That is necessary as APICompat is invoked twice, once for the ref <-> src comparision and then again
       for the package validation (which doesn't include reference assemblies). As both operations don't have
       all the inputs available, some suppressions might only apply to one or the other and hence unnecessary
       suppressions can't be determined.
       Disable the validation under source build as that might use an out-of-date SDK and not the ApiCompat.Task package. -->
  <PropertyGroup Condition="('$(IsPackable)' == 'true' and '$(IsRuntimeAndReferenceAssembly)' != 'true') or '$(DotNetBuildSourceOnly)' == 'true'">
    <ApiCompatPreserveUnnecessarySuppressions>true</ApiCompatPreserveUnnecessarySuppressions>
    <ApiCompatPermitUnnecessarySuppressions>true</ApiCompatPermitUnnecessarySuppressions>
  </PropertyGroup>

  <PropertyGroup Condition="'$(IsCrossTargetingBuild)' != 'true'">
    <!-- Disable API compat if the project doesn't have a reference assembly. -->
    <ApiCompatValidateAssemblies Condition="'$(HasMatchingContract)' != 'true'">false</ApiCompatValidateAssemblies>
    <!-- TODO: Move into Microsoft.DotNet.GenFacadesNotSupported.targets. -->
    <!-- Not supported sources are created from the ref assembly, we currently don't produce finalizers in dummy assemblies, so we disable ApiCompat to not fail. -->
    <ApiCompatValidateAssemblies Condition="'$(GeneratePlatformNotSupportedAssemblyMessage)' != ''">false</ApiCompatValidateAssemblies>
  </PropertyGroup>

  <ItemGroup>
    <ApiCompatExcludeAttributesFile Include="$(RepositoryEngineeringDir)DefaultGenApiDocIds.txt" />
    <ApiCompatExcludeAttributesFile Include="$(RepositoryEngineeringDir)ApiCompatExcludeAttributes.txt" />
  </ItemGroup>

  <ItemGroup>
    <!-- Transform the API Compat assemblies passed in to log-able strings. -->
    <ApiCompatLeftAssembliesTransformationPattern Include="$(_ApiCompatCaptureGroupPattern)" ReplacementString="ref/$1/$2" />
    <ApiCompatLeftAssembliesTransformationPattern Include="$(_ApiCompatRuntimePrefixPattern)" ReplacementString="runtimes/$3/$1/$2/$4" />
    <ApiCompatLeftAssembliesTransformationPattern Include="runtimes/windows/" ReplacementString="runtimes/win/" />

    <ApiCompatRightAssembliesTransformationPattern Include="$(_ApiCompatCaptureGroupPattern)" ReplacementString="$(_ApiCompatLibReplacementString)" />
    <ApiCompatRightAssembliesTransformationPattern Include="$(_ApiCompatRuntimePrefixPattern)" ReplacementString="runtimes/$3/$1/$2/$4" />
    <ApiCompatRightAssembliesTransformationPattern Include="runtimes/windows/" ReplacementString="runtimes/win/" />

    <!-- Fall back to the targeting pack dir for NetCoreAppCurrent to avoid passing through dependencies from ref to src. -->
    <ApiCompatContractAssemblyReferences Include="$(MicrosoftNetCoreAppRefPackRefDir)"
                                         Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '$(NetCoreAppCurrent)'))" />
  </ItemGroup>

    <!-- Use the apicompat task package instead of the in-built SDK functionality to consume newer features. -->
  <ItemGroup Condition="'$(EnablePackageValidation)' == 'true' or
                        '$(ApiCompatValidateAssemblies)' == 'true'">
    <PackageReference Include="Microsoft.DotNet.ApiCompat.Task" Version="$(MicrosoftDotNetApiCompatTaskVersion)" PrivateAssets="all" IsImplicitlyDefined="true" />
  </ItemGroup>

  <!-- ##### GenFacades settings ##### -->
  <PropertyGroup>
    <!-- Let GenFacades use roslyn from the toolset package as it loads sources which might require newer language features. -->
    <GenFacadesUseRoslynToolsetPackagePath>true</GenFacadesUseRoslynToolsetPackagePath>
  </PropertyGroup>

  <!-- ##### GenAPI settings ##### -->

  <PropertyGroup>
    <GenAPIExcludeAttributesList>$(RepositoryEngineeringDir)DefaultGenApiDocIds.txt</GenAPIExcludeAttributesList>
    <GenAPIHeaderFile>$(RepositoryEngineeringDir)LicenseHeader.txt</GenAPIHeaderFile>
    <GenAPITargetPath>$([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '..', 'ref', '$(AssemblyName).cs'))</GenAPITargetPath>
    <GenAPILangVersion Condition="'$(LangVersion)' != ''">$(LangVersion)</GenAPILangVersion>
    <ProjectForGenAPIDocIdGeneration Condition="'$(ProjectForGenAPIDocIdGeneration)' == ''">$(CoreLibProject)</ProjectForGenAPIDocIdGeneration>
  </PropertyGroup>

  <ItemGroup>
    <!-- GenAPI is currently only invoked on demand and not used as part of the build as it isn't source build compatible. -->
    <PackageReference Include="Microsoft.DotNet.GenAPI"
                      Version="$(MicrosoftDotNetGenApiVersion)"
                      PrivateAssets="all"
                      IsImplicitlyDefined="true"
                      Condition="'$(DotNetBuildSourceOnly)' != 'true'" />
  </ItemGroup>

  <!-- Generate a .txt file with all public types of the project referenced by ProjectForGenAPIDocIdGeneration (e.g. System.Private.CoreLib or System.Private.Uri implementation assembly).
       This file is then later on passed to GenAPI as the list of types which should be "split out" with the conditional compilation logic. -->
  <Target Name="GenerateConditionalTypeLists"
          Condition="'@(ProjectReference)' != '' and @(ProjectReference->AnyHaveMetadataValue('Identity', '$(ProjectForGenAPIDocIdGeneration)')) and ('$(ContractTypesPartiallyMoved)' == 'true' or '$(ContractTypesPartiallyMoved)' == 'true')">
    <PropertyGroup>
      <!-- We either wrap all contract types in a #if condition (when SymbolForGenAPIConditionalTypeLists is empty),
           or just the types mentioned in GenAPIConditionalTypeLists with the symbol specified in SymbolForGenAPIConditionalTypeLists -->
      <GenAPIDefaultCondition Condition="'$(SymbolForGenAPIConditionalTypeLists)' == ''">!BUILDING_CORELIB_REFERENCE</GenAPIDefaultCondition>
    </PropertyGroup>

    <ItemGroup>
      <_genAPILibPathForGenAPIDocIdGeneration Include="@(ReferencePath->'%(RootDir)%(Directory)'->Distinct())" />
      <_genAPIResolvedProjectForGenAPIDocIdGeneration Include="@(_ResolvedProjectReferencePaths->WithMetadataValue('MSBuildSourceProjectFile','$(ProjectForGenAPIDocIdGeneration)'))" />

      <GenAPIConditionalTypeLists Include="$(IntermediateOutputPath)GenAPIConditionalTypeList.txt">
        <Symbol Condition="'$(SymbolForGenAPIConditionalTypeLists)' != ''">$(SymbolForGenAPIConditionalTypeLists)</Symbol>
      </GenAPIConditionalTypeLists>
    </ItemGroup>

    <!-- Generate conditional compilation symbols to hide types implemented in contracts so we can include the .cs in the System.Private.CoreLib ref -->
    <Microsoft.DotNet.GenAPI.GenAPITask
        Assembly="@(_genAPIResolvedProjectForGenAPIDocIdGeneration)"
        LibPath="@(_genAPILibPathForGenAPIDocIdGeneration)"
        WriterType="DocIds"
        DocIdKinds="Namespace, Type"
        OutputPath="@(GenAPIConditionalTypeLists)" />
  </Target>

  <Target Name="SetGenAPIProperties"
          DependsOnTargets="GenerateConditionalTypeLists"
          BeforeTargets="GenerateReferenceAssemblySource">
    <PropertyGroup>
      <GenAPIFollowTypeForwards Condition="'%(ProjectReference.Identity)' == '$(CoreLibProject)' or '%(ProjectReference.Identity)' == '$(UriProject)'">true</GenAPIFollowTypeForwards>
    </PropertyGroup>
  </Target>

  <Import Project="$(RepositoryEngineeringDir)outerBuild.targets" Condition="'$(IsCrossTargetingBuild)' == 'true'" />
</Project>
